#include <transform.h>

Mat4x4 scale(const Mat4x4& mat, const Vec3f& scaleVec) {
    Mat4x4 scaleMat ({
        scaleVec.x, 0,          0,          0,
        0,          scaleVec.y, 0,          0,
        0,          0,          scaleVec.z, 0,
        0,          0,          0,          1
    });

    return scaleMat * mat;
}

Mat4x4 rotate(const Mat4x4& mat, float angle, const Vec3f& axis) {
    Mat4x4 rotateMat;
    rotateMat(0, 0) = axis.x * axis.x * (1 - cosf(angle)) + cosf(angle);
    rotateMat(0, 1) = axis.x * axis.y * (1 - cosf(angle)) + axis.z * sinf(angle);
    rotateMat(0, 2) = axis.x * axis.z * (1 - cosf(angle)) - axis.y * sinf(angle);
    rotateMat(0, 3) = 0.0f;

    rotateMat(1, 0) = axis.x * axis.y * (1 - cosf(angle)) - axis.z * sinf(angle);
    rotateMat(1, 1) = axis.y * axis.y * (1 - cosf(angle)) + cosf(angle);
    rotateMat(1, 2) = axis.y * axis.z * (1 - cosf(angle)) + axis.x * sinf(angle);
    rotateMat(1, 3) = 0.0f;

    rotateMat(2, 0) = axis.x * axis.z * (1 - cosf(angle)) + axis.y * sinf(angle);
    rotateMat(2, 1) = axis.y * axis.z * (1 - cosf(angle)) - axis.x * sinf(angle);
    rotateMat(2, 2) = axis.z * axis.z * (1 - cosf(angle)) + cosf(angle);
    rotateMat(2, 3) = 0.0f;

    rotateMat(3, 0) = 0.0f;
    rotateMat(3, 1) = 0.0f;
    rotateMat(3, 2) = 0.0f;
    rotateMat(3, 3) = 1.0f;

    return rotateMat * mat;
}

Mat4x4 rotate(float xAngel, float yAngel, float zAngel) {
    Mat4x4 xRotateMat ({
        1,  0,            0,            0,
        0,  cosf(xAngel), sinf(yAngel), 0,
        0, -sinf(xAngel), cosf(xAngel), 0,
        0,  0,            0,            1
    });

    Mat4x4 yRotateMat ({
        cosf(yAngel), 0, -sinf(yAngel), 0,
        0,            1,  0,            0,
        sinf(yAngel), 0,  cosf(yAngel), 0,
        0,            0,  0,            1
    });

    Mat4x4 zRotateMat ({
         cosf(zAngel), sinf(zAngel), 0, 0,
        -sinf(zAngel), cosf(zAngel), 0, 0,
         0,            0,            1, 0,
         0,            0,            0, 1
    });

    return xRotateMat * yRotateMat * zRotateMat;
}

Mat4x4 translate(const Mat4x4& mat, const Vec3f& pos) {
    Mat4x4 translateMat ({
        1, 0, 0, pos.x,
        0, 1, 0, pos.y,
        0, 0, 1, pos.z,
        0, 0, 0, 1
    });
    return translateMat * mat;
}

Mat4x4 lookAt(const Vec3f& pos, const Vec3f& center, const Vec3f& up) {
    // right hand coordinate
    // col major
    Vec3f w = normalize(pos - center); // camera forward
    Vec3f u = normalize(cross(up, w)); // camera left
    Vec3f v = cross(w, u);             // camera up
    
    if (cross(u, v) * w > 0.0f) {
        printf("camera coordiate is right hand\n");
    }
    else {
        printf("camera coordiate is left hand\n");
    }

    float Qu = -(u * pos);
    float Qv = -(v * pos);
    float Qw = -(w * pos);
    
    return Mat4x4({
        u.x, u.y, u.z, Qu,
        v.x, v.y, v.z, Qv,
        w.x, w.y, w.z, Qw,
        0, 0, 0, 1
    });
}

// fov -> 水平视场角
// aspect -> 宽高比
// near -> 近平面到相机距离(总为正)
// far -> 远平面到相机距离(总为正)
Mat4x4 perspective(float fov, float aspect, float near, float far) {
    float l = -near * tanf(fov / 2);
    float r = -l;
    float b = r / aspect;
    float t = -b;

    return Mat4x4({
        2 * near / (r - l), 0, (r + l) / (r - l), 0,
        0, 2 * near / (t - b), (t + b) / (t - b), 0,
        0, 0, -(far + near) / (far - near), -2 * near * far / (far - near),
        0, 0, -1, 0
    });
}

Mat4x4 viewport(int width, int height) {
    return Mat4x4 ({
        (float)width / 2, 0,                 0, (float)width / 2,
        0,                (float)height / 2, 0, (float)height / 2,
        0,                0,                 1, 0,
        0,                0,                 0, 1
    });
}
